use redis::AsyncCommands;
use tokio::sync::{mpsc, oneshot};

use crate::types::channel;

pub async fn start(mut rx: mpsc::Receiver<(channel::Request, oneshot::Sender<channel::Response>)>) {
    let mut client = Client::new().await;

    loop {
        let (request, rsp_tx) = rx.recv().await.unwrap();
        match request {
            channel::Request::TokenSet(token_info) => {
                match client
                    .setex(token_info.token, token_info.user_id, token_info.expiration)
                    .await
                {
                    Ok(_) => {
                        let _ = rsp_tx.send(channel::Response::Ok());
                    }
                    Err(err) => {
                        let _ = rsp_tx.send(channel::Response::Error(err));
                    }
                }
            }

            channel::Request::TokenGet(token) => match client.get(token).await {
                Ok(value) => {
                    let _ = rsp_tx.send(channel::Response::TokenGet(value));
                }
                Err(err) => {
                    let _ = rsp_tx.send(channel::Response::Error(err));
                }
            },

            channel::Request::TokenDelete(token) => match client.del(token).await {
                Ok(_) => {
                    let _ = rsp_tx.send(channel::Response::Ok());
                }
                Err(err) => {
                    let _ = rsp_tx.send(channel::Response::Error(err));
                }
            },

            _ => rsp_tx
                .send(channel::Response::Error(
                    "Invalid request type to redis client".to_string(),
                ))
                .expect("Redis client respond channel request failed"),
        }
    }
}

struct Client {
    connections: redis::aio::MultiplexedConnection,
}

impl Client {
    async fn new() -> Self {
        let client = redis::Client::open("redis://redis:6379").unwrap();

        Client {
            connections: client
                .get_multiplexed_tokio_connection()
                .await
                .expect("Connect to Redis failed"),
        }
    }

    async fn setex(&mut self, key: String, value: String, expiration: u64) -> Result<(), String> {
        println!("setex: {:?}:{:?}:{:?}", key, value, expiration);
        match self
            .connections
            .set_ex::<String, String, String>(key, value, expiration)
            .await
        {
            Ok(_) => Ok(()),
            Err(err) => Err(err.to_string()),
        }
    }

    async fn get(&mut self, key: String) -> Result<String, String> {
        // let result = self.connections.get("tt").await;
        match self.connections.get::<String, String>(key).await {
            Ok(value) => Ok(value),
            Err(err) => Err(err.to_string()),
        }
    }

    async fn del(&mut self, key: String) -> Result<(), String> {
        // let result = self.connections.get("tt").await;
        match self.connections.del::<String, String>(key).await {
            Ok(_) => Ok(()),
            Err(err) => Err(err.to_string()),
        }
    }
}
